package ibbolt

import (
	"errors"
	"iris-wms/common/tools"
	"os"
	"sync/atomic"
	"time"

	"github.com/kataras/golog"
	"go.etcd.io/bbolt"
)

var GBbolt *Bbolt

type Bbolt struct {
	Service    *bbolt.DB
	BucketName []byte
}

func InitBblot() {
	var (
		err        error
		bucketName = []byte(tools.GConfig.Boot.SystemBucket)
		db         *bbolt.DB
	)
	// Timeout only on Darwin and Linux valid
	// wait file-lock time
	if db, err = bbolt.Open(tools.GConfig.Boot.BboltFilename, os.FileMode(0750), &bbolt.Options{Timeout: 10 * time.Second}); err != nil {
		goto ERR
	}

	if err = db.Update(func(tx *bbolt.Tx) error {
		if _, err = tx.CreateBucketIfNotExists(bucketName); err != nil {
			if errors.Is(err, bbolt.ErrBucketNameRequired) {
				return nil
			} else {
				return err
			}
		}
		return nil
	}); err != nil {
		goto ERR
	}
	GBbolt = &Bbolt{
		Service:    db,
		BucketName: bucketName,
	}
	return
ERR:
	golog.Fatalf("~~> 初始化文件数据库错误,原因:%s", err.Error())
}

func (s *Bbolt) PutUint(key string, value uint) error {
	var err error
	if err = s.Service.Update(func(tx *bbolt.Tx) error {
		bucket := tx.Bucket(s.BucketName)
		if err = bucket.Put([]byte(key), tools.UintToBytes(uint(value))); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

func (s *Bbolt) GetUint(key string) (uint, error) {
	var value uint
	err := s.Service.Update(func(tx *bbolt.Tx) error {
		bucket := tx.Bucket(s.BucketName)
		value = tools.BytesToUint(bucket.Get([]byte(key)))
		return nil
	})
	if err != nil {
		return 0, err
	}
	return value, nil
}

// step=1
func (s *Bbolt) Incr(key string) (uint, error) {
	var value uint64
	if err := s.Service.Update(func(tx *bbolt.Tx) error {
		bucket := tx.Bucket(s.BucketName)
		value = uint64(tools.BytesToUint(bucket.Get([]byte(key))))
		atomic.AddUint64(&value, 1)
		if value >= 100000 {
			value = 0
		}
		if err := bucket.Put([]byte(key), tools.UintToBytes(uint(value))); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return 0, err
	}
	return uint(value), nil
}
